package wci.frontend.pascal.parsers;

import static wci.frontend.pascal.PascalErrorCode.INVALID_INDEX_TYPE;
import static wci.frontend.pascal.PascalErrorCode.MISSING_COMMA;
import static wci.frontend.pascal.PascalErrorCode.MISSING_LEFT_BRACKET;
import static wci.frontend.pascal.PascalErrorCode.MISSING_OF;
import static wci.frontend.pascal.PascalErrorCode.MISSING_RIGHT_BRACKET;
import static wci.frontend.pascal.PascalTokenType.COMMA;
import static wci.frontend.pascal.PascalTokenType.LEFT_BRACKET;
import static wci.frontend.pascal.PascalTokenType.OF;
import static wci.frontend.pascal.PascalTokenType.RIGHT_BRACKET;
import static wci.frontend.pascal.PascalTokenType.SEMICOLON;
import static wci.intermediate.typeimpl.TypeFormImpl.ARRAY;
import static wci.intermediate.typeimpl.TypeFormImpl.ENUMERATION;
import static wci.intermediate.typeimpl.TypeFormImpl.SUBRANGE;
import static wci.intermediate.typeimpl.TypeKeyImpl.ARRAY_ELEMENT_COUNT;
import static wci.intermediate.typeimpl.TypeKeyImpl.ARRAY_ELEMENT_TYPE;
import static wci.intermediate.typeimpl.TypeKeyImpl.ARRAY_INDEX_TYPE;
import static wci.intermediate.typeimpl.TypeKeyImpl.ENUMERATION_CONSTANTS;
import static wci.intermediate.typeimpl.TypeKeyImpl.SUBRANGE_MAX_VALUE;
import static wci.intermediate.typeimpl.TypeKeyImpl.SUBRANGE_MIN_VALUE;

import java.util.ArrayList;
import java.util.EnumSet;

import wci.frontend.Token;
import wci.frontend.TokenType;
import wci.frontend.pascal.PascalParserTD;
import wci.frontend.pascal.PascalTokenType;
import wci.intermediate.SymTabEntry;
import wci.intermediate.TypeFactory;
import wci.intermediate.TypeForm;
import wci.intermediate.TypeSpec;

/**
 * <h1>ArrayTypeParser</h1>
 * <p>解析一个数组说明，两种方式的多维</p>
 * <p>Copyright (c) 2009 by Ronald Mak</p>
 * <p>For instructional purposes only.  No warranties.</p>
 */
class ArrayTypeParser extends TypeSpecificationParser
{
    protected ArrayTypeParser(PascalParserTD parent)
    {
        super(parent);
    }

    //数组 左方括号之后的同步集
    private static final EnumSet<PascalTokenType> LEFT_BRACKET_SET =
        SimpleTypeParser.SIMPLE_TYPE_START_SET.clone();
    static {
        LEFT_BRACKET_SET.add(LEFT_BRACKET);
        LEFT_BRACKET_SET.add(RIGHT_BRACKET);
    }

    // 右方括号之后的同步集
    private static final EnumSet<PascalTokenType> RIGHT_BRACKET_SET =
        EnumSet.of(RIGHT_BRACKET, OF, SEMICOLON);

    // 相当于元素类型的同步集
    private static final EnumSet<PascalTokenType> OF_SET =
        TypeSpecificationParser.TYPE_START_SET.clone();
    static {
        OF_SET.add(OF);
        OF_SET.add(SEMICOLON);
    }

    public TypeSpec parse(Token token)
        throws Exception
    {
        TypeSpec arrayType = TypeFactory.createType(ARRAY);
        token = nextToken();  //剔掉ARRAY关键字

        //在左括号出同步一下
        token = synchronize(LEFT_BRACKET_SET);
        if (token.getType() != LEFT_BRACKET) {
            errorHandler.flag(token, MISSING_LEFT_BRACKET, this);
        }

        // 解析索引类型
        TypeSpec elementType = parseIndexTypeList(token, arrayType);

        // 在右括号出同步一下
        token = synchronize(RIGHT_BRACKET_SET);
        if (token.getType() == RIGHT_BRACKET) {
            token = nextToken();  
        }
        else {
            errorHandler.flag(token, MISSING_RIGHT_BRACKET, this);
        }

        // 在OF处同步一下
        token = synchronize(OF_SET);
        if (token.getType() == OF) {
            token = nextToken(); 
        }
        else {
            errorHandler.flag(token, MISSING_OF, this);
        }

        // 然后是元素类型
        elementType.setAttribute(ARRAY_ELEMENT_TYPE, parseElementType(token));

        return arrayType;
    }

    //索引同步集
    private static final EnumSet<PascalTokenType> INDEX_START_SET =
        SimpleTypeParser.SIMPLE_TYPE_START_SET.clone();
    static {
        INDEX_START_SET.add(COMMA);
    }

    // 索引结束后的同步集
    private static final EnumSet<PascalTokenType> INDEX_END_SET =
        EnumSet.of(RIGHT_BRACKET, OF, SEMICOLON);

    // 索引之后的同步集
    private static final EnumSet<PascalTokenType> INDEX_FOLLOW_SET =
        INDEX_START_SET.clone();
    static {
        INDEX_FOLLOW_SET.addAll(INDEX_END_SET);
    }

    /**
     * 解析索引类型说明列表，比如 ARRAY [1..3] 里面的 1..3
     * @param token 当前Token，即左括号
     * @param arrayType 父的数组类型说明
     * @return 元素类型说明
     * @throws Exception
     */
    private TypeSpec parseIndexTypeList(Token token, TypeSpec arrayType)
        throws Exception
    {
        TypeSpec elementType = arrayType;
        boolean anotherIndex = false;

        token = nextToken();  // 剔掉左括号

        // 解析索引类型说明列表，多维如 ARRAY [1..3, 'a'..'z'] 等
        do {
            anotherIndex = false;

            //索引类型
            token = synchronize(INDEX_START_SET);
            parseIndexType(token, elementType);

            //索引结束同步一下
            token = synchronize(INDEX_FOLLOW_SET);
            TokenType tokenType = token.getType();
            if ((tokenType != COMMA) && (tokenType != RIGHT_BRACKET)) {
                if (INDEX_START_SET.contains(tokenType)) {
                    errorHandler.flag(token, MISSING_COMMA, this);
                    anotherIndex = true;
                }
            }else if (tokenType == COMMA) { //一个逗号以为这多维索引
                TypeSpec newElementType = TypeFactory.createType(ARRAY);
                elementType.setAttribute(ARRAY_ELEMENT_TYPE, newElementType);
                elementType = newElementType;

                token = nextToken(); //吞掉逗号
                anotherIndex = true;
            }
        } while (anotherIndex);

        return elementType;
    }

    /**
     * 解析索引类型说明，这个是单个的 []包含的，一定是个简单类型，不包含嵌套。
     * @param token 
     * @param 传入的数组类型说明，解析过程会加上一些属性
     * @throws Exception
     */
    private void parseIndexType(Token token, TypeSpec arrayType)
        throws Exception
    {
        SimpleTypeParser simpleTypeParser = new SimpleTypeParser(this);
        TypeSpec indexType = simpleTypeParser.parse(token);
        arrayType.setAttribute(ARRAY_INDEX_TYPE, indexType);

        if (indexType == null) {
            return;
        }

        TypeForm form = indexType.getForm();
        int count = 0;

        // 如果是子界类型
        if (form == SUBRANGE) {
            Integer minValue =
                (Integer) indexType.getAttribute(SUBRANGE_MIN_VALUE);
            Integer maxValue =
                (Integer) indexType.getAttribute(SUBRANGE_MAX_VALUE);

            if ((minValue != null) && (maxValue != null)) {
                count = maxValue - minValue + 1;
            }
        }//枚举类型
        else if (form == ENUMERATION) {
            ArrayList<SymTabEntry> constants = (ArrayList<SymTabEntry>)
                indexType.getAttribute(ENUMERATION_CONSTANTS);
            count = constants.size();
        }
        else {
            errorHandler.flag(token, INVALID_INDEX_TYPE, this);
        }
        arrayType.setAttribute(ARRAY_ELEMENT_COUNT, count);
    }

    /**
     * 数组元素可以是另外一个数组，也可是个简单类型，交给TypeSpec来决定吧。
     * @param token 当前token， OF之后的第一个
     * @return 元素类型说明
     * @throws Exception
     */
    private TypeSpec parseElementType(Token token)
        throws Exception
    {
        TypeSpecificationParser typeSpecificationParser =
            new TypeSpecificationParser(this);
        return typeSpecificationParser.parse(token);
    }
}
